﻿using System.CodeDom;
using System.CodeDom.Compiler;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Web.Razor;
using System.Web.Razor.Generator;
using System.Web.Razor.Parser;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using PropertyExpression.ControllableQuery;
using Microsoft.CSharp;

namespace CQTool
{
    /// <summary>
    /// http://mnaoumov.wordpress.com/2012/09/26/developing-custom-tool-aka-single-file-generators-for-visual-studio-2012/
    /// </summary>
    [ComVisible(true)]
    // TODO: Replace with your GUID
    [Guid(Guid)]
    public class CQToolGenerator : BaseCodeGeneratorWithSite
    {
        public const string Guid = "8F6136FB-3EFF-4B43-A85C-A8803BE215E5";
        public const string Name = "CQTool";

        public override string GetDefaultExtension()
        {
            // TODO: Replace with your implementation
            return ".generated.cs";
        }

        private string GetClassName()
        {
            return ParserHelpers.SanitizeClassName(Path.GetFileNameWithoutExtension(InputFilePath));
        }

        private class RazorEngineHost : System.Web.Razor.RazorEngineHost
        {
            public RazorEngineHost(RazorCodeLanguage codeLanguage)
                : base(codeLanguage)
            {
            }

            public override void PostProcessGeneratedCode(CodeGeneratorContext context)
            {
                base.PostProcessGeneratedCode(context);
                context.GeneratedClass.IsPartial = true;
                context.GeneratedClass.Members.Remove(
                    context.GeneratedClass.Members.OfType<CodeConstructor>().Single());
                context.GeneratedClass.Members.Remove(context.TargetMethod);
            }
        }

        protected override byte[] GenerateCode(string inputFileName, string inputFileContent)
        {
            var host = new RazorEngineHost(new CSharpRazorCodeLanguage()) {
                GeneratedClassContext =
                    new GeneratedClassContext(executeMethodName: GeneratedClassContext.Default.ExecuteMethodName,
                        writeMethodName: GeneratedClassContext.Default.WriteMethodName,
                        writeLiteralMethodName: GeneratedClassContext.Default.WriteLiteralMethodName,
                        writeToMethodName: Util.WriteToMethodName, writeLiteralToMethodName: Util.WriteLiteralToMethodName,
                        templateTypeName: typeof (QFragment).FullName,
                        defineSectionMethodName: GeneratedClassContext.Default.DefineSectionMethodName),
                DefaultNamespace = FileNamespace, StaticHelpers = true, DefaultClassName = GetClassName()
            };
            var engine = new RazorTemplateEngine(host);
            GeneratorResults razorResult;
            using (var reader = new StringReader(inputFileContent))
                razorResult = engine.GenerateCode(reader, className: null, rootNamespace: null,
                    sourceFileName: Path.GetFileName(inputFileName));
            string result;
            if (!razorResult.Success)
            {
                var builder = new StringBuilder();
                foreach (var error in razorResult.ParserErrors)
                {
                    GeneratorErrorCallback(false, 0, error.Message, error.Location.LineIndex + 1,
                        error.Location.CharacterIndex + 1);
                    builder.AppendFormat("ERROR! (Ln {0}, Col {1}) {2}",
                        error.Location.LineIndex + 1,
                        error.Location.CharacterIndex + 1,
                        error.Message);
                    builder.AppendLine();
                }
                result = builder.ToString();
            }
            else
            {
                StringWriter stringWriter;
                using (stringWriter = new StringWriter())
                using (var codeProvider = new CSharpCodeProvider())
                    codeProvider.GenerateCodeFromCompileUnit(
                        razorResult.GeneratedCode, stringWriter, new CodeGeneratorOptions());
                result = stringWriter.ToString();
            }
            return Encoding.UTF8.GetBytes(result);
        }
    }
}